package persistencia;

import DTOs.RegistroTraduccionDTO;
import Exepciones.ExcepcionesSys.ExcepcionConexionErrorSys;
import Exepciones.ExcepcionesSys.ExcepcionConsultaErrorSys;
import Exepciones.ExcepcionesSys.ExcepcionDeleteErrorSys;
import Exepciones.ExcepcionesSys.ExcepcionInsertErrorSys;
import Exepciones.ExcepcionesSys.ExcepcionUpdateErrorSys;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 * Clase que abstrae todo el comportamiento en comun de las clases de acceso traduccionDAO
 */
public abstract class AbstractRegistroTraduccionDAO implements RegistroTraduccionDAO {

    protected DAOFactory daoFactory;
    private Connection conexionDB;
    protected String insertarQuery = ""
            + "INSERT INTO registrotraduccion(textoOrigen, idiomaOrigen, textoDestino, idiomaDestino)"
            + "SELECT ?,?,?,?"
            + "FROM VERSION()"
            + "WHERE NOT EXISTS((SELECT * FROM registrotraduccion WHERE textoOrigen=?"
            + "                                                     AND idiomaOrigen=?"
            + "                                                     AND textoDestino=?"
            + "                                                     AND idiomaDestino=?)"
            + "                 UNION"
            + "                 (SELECT * FROM registrotraduccion WHERE textoOrigen=?"
            + "                                                     AND idiomaOrigen=?"
            + "                                                     AND textoDestino=?"
            + "                                                     AND idiomaDestino=?));";
    protected String eliminarQuery = ""
            + " DELETE FROM registrotraduccion"
            + " WHERE textoOrigen=?"
            + "     AND idiomaOrigen=?"
            + "     AND textoDestino=?"
            + "     AND idiomaDestino=?"
            + "     OR (textoOrigen=?"
            + "         AND idiomaOrigen=?"
            + "         AND textoDestino=?"
            + "         AND idiomaDestino=?)";
    protected String modificarQuery = ""
            + " UPDATE registrotraduccion"
            + " SET textoDestino=?, idiomaDestino=?, textoOrigen=?, idiomaOrigen=?"
            + " WHERE  textoOrigen=?"
            + "     AND idiomaOrigen=?"
            + "     AND textoDestino=?"
            + "     AND idiomaDestino=?"
            + "     OR (textoOrigen=?"
            + "         AND idiomaOrigen=?"
            + "         AND textoDestino=?"
            + "         AND idiomaDestino=?)";
    protected String buscarTraduccionQuery = ""
            + " SELECT * FROM registrotraduccion"
            + " WHERE textoOrigen=?"//1-textoOrigen
            + " AND idiomaOrigen=?"//2-idiomaOrigen
            + " AND idiomaDestino=?"//3-idiomaDestino
            + " OR textoDestino  IN (SELECT textoDestino"
            + "                      FROM registrotraduccion"
            + "                      WHERE idiomaOrigen=?"//4-idiomaDestino
            + "                            AND textoDestino=?"//5-textoOrigen
            + "                            AND idiomaDestino=?)";//6-idiomaOrigen
    protected String buscarAproximacionQuery = ""
            + "SELECT r.textoOrigen,r.idiomaorigen, r.textodestino, r.idiomadestino"
            + " FROM((SELECT * FROM registrotraduccion"
            + "         WHERE idiomaOrigen=?"//1-idiomaOrigen
            + "             AND (textoOrigen LIKE ?"//2-TextoOrigen
            + "                     OR textoOrigen LIKE ?"//3-TextoOrigen
            + "                     OR textoOrigen LIKE ?))"//4-TextoOrigen
            + "     UNION"
            + "     (SELECT * FROM registrotraduccion"
            + "         WHERE idiomaDestino=?"//5-idiomaOrigen
            + "             AND (textoDestino LIKE ?"//6-TextoOrigen
            + "                     OR textoDestino LIKE ?"//7-TextoOrigen
            + "                     OR textoDestino LIKE ?))) AS r"//8-TextoOrigen
            + " WHERE r.idiomaDestino=? "//9-idiomaDestino
            + "       OR r.idiomaOrigen=?";//10-idiomaDestino

    /**
     * Inserta en la Factory actual una nueva traducción.
     * @param textoOrigen -Texto a traducir
     * @param idiomaOrigen -Sigla del idioma del que parte la traducción
     * @param idiomaDestino -Sigla del idioma al que se desea la traducción
     * @param textoDestino -Texto ya traducido
     * @throws ExcepcionConexionErrorSys
     * @throws ExcepcionInsertErrorSys
     */
    public void insertar(String textoOrigen, String idiomaOrigen, String textoDestino, String idiomaDestino) throws ExcepcionConexionErrorSys, ExcepcionInsertErrorSys {
        textoOrigen = textoOrigen.toLowerCase();
        idiomaOrigen = idiomaOrigen.toLowerCase();
        textoDestino = textoDestino.toLowerCase();
        idiomaDestino = idiomaDestino.toLowerCase();
        try {
            conexionDB = daoFactory.getConexion();
            conexionDB.setAutoCommit(false);
            String query = this.insertarQuery;
            PreparedStatement preStat = conexionDB.prepareStatement(query);
            preStat.setString(1, textoOrigen);
            preStat.setString(2, idiomaOrigen);
            preStat.setString(3, textoDestino);
            preStat.setString(4, idiomaDestino);
            preStat.setString(5, textoOrigen);
            preStat.setString(6, idiomaOrigen);
            preStat.setString(7, textoDestino);
            preStat.setString(8, idiomaDestino);
            preStat.setString(9, textoDestino);
            preStat.setString(10, idiomaDestino);
            preStat.setString(11, textoOrigen);
            preStat.setString(12, idiomaOrigen);
            preStat.executeUpdate();

            conexionDB.commit();
        } catch (SQLException ex) {
            throw new ExcepcionInsertErrorSys();
        } finally {
            daoFactory.cerrarConexion(conexionDB);
        }
    }

    /**
     * Elimina de la Factory actuál una tradución en particular
     * @param textoOrigen Texto a traducir
     * @param idiomaOrigen Sigla del idioma del que parte la traducción
     * @param idiomaDestino Sigla del idioma al que se desea la traducción
     * @param textoDestino Texto ya traducido
     * @throws ExcepcionConexionErrorSys
     * @throws ExcepcionDeleteErrorSys
     */
    public void eliminar(String textoOrigen, String idiomaOrigen, String textoDestino, String idiomaDestino) throws ExcepcionConexionErrorSys, ExcepcionDeleteErrorSys {
        textoOrigen = textoOrigen.toLowerCase();
        idiomaOrigen = idiomaOrigen.toLowerCase();
        textoDestino = textoDestino.toLowerCase();
        idiomaDestino = idiomaDestino.toLowerCase();
        try {
            conexionDB = daoFactory.getConexion();
            conexionDB.setAutoCommit(false);
            String queryEliminar = this.eliminarQuery;
            PreparedStatement preStat = conexionDB.prepareStatement(queryEliminar);
            preStat.setString(1, textoOrigen);
            preStat.setString(2, idiomaOrigen);
            preStat.setString(3, textoDestino);
            preStat.setString(4, idiomaDestino);
            preStat.setString(5, textoDestino);
            preStat.setString(6, idiomaDestino);
            preStat.setString(7, textoOrigen);
            preStat.setString(8, idiomaOrigen);
            preStat.executeUpdate();
            conexionDB.commit();
        } catch (SQLException ex) {
            throw new ExcepcionDeleteErrorSys();
        } finally {
            daoFactory.cerrarConexion(conexionDB);
        }
    }

    /**
     * Modifica de la Factory actual una traducción en particular
     * @param textoOrigen Texto a traducir
     * @param idiomaOrigen Sigla del idioma del que parte la traducción
     * @param textoDestinoNew Texto traducido ya actualizado
     * @param textoDestinoOld Texto traducido para actualizar
     * @param idiomaDestino Sigla del idioma al que se desea la traducción
     * @throws ExcepcionConexionErrorSys
     * @throws ExcepcionUpdateErrorSys
     */
    public void modificar(String textoOrigen, String idiomaOrigen, String textoDestinoNew, String textoDestinoOld, String idiomaDestino) throws ExcepcionConexionErrorSys, ExcepcionUpdateErrorSys {
        textoOrigen = textoOrigen.toLowerCase();
        idiomaOrigen = idiomaOrigen.toLowerCase();
        textoDestinoNew = textoDestinoNew.toLowerCase();
        textoDestinoOld = textoDestinoOld.toLowerCase();
        idiomaDestino = idiomaDestino.toLowerCase();
        try {
            conexionDB = daoFactory.getConexion();
            conexionDB.setAutoCommit(false);
            String queryNew = this.modificarQuery;
            PreparedStatement preStat = conexionDB.prepareStatement(queryNew);
            preStat.setString(1, textoDestinoNew);
            preStat.setString(2, idiomaDestino);
            preStat.setString(3, textoOrigen);
            preStat.setString(4, idiomaOrigen);
            preStat.setString(5, textoOrigen);
            preStat.setString(6, idiomaOrigen);
            preStat.setString(7, textoDestinoOld);
            preStat.setString(8, idiomaDestino);
            preStat.setString(9, textoDestinoOld);
            preStat.setString(10, idiomaDestino);
            preStat.setString(11, textoOrigen);
            preStat.setString(12, idiomaOrigen);
            preStat.executeUpdate();

            conexionDB.commit();
        } catch (SQLException ex) {
            throw new ExcepcionUpdateErrorSys();
        } finally {
            daoFactory.cerrarConexion(conexionDB);
        }
    }

    /**
     * Obtiene de la Factory actual la traducción exacta para el texto de entrada.
     * Acepta que una traducción para un texto de origen dado que posea mas de un significado.
     * Soporta sinónimos.
     * @param textoOrigen -Texto a traducir
     * @param idiomaOrigen -Sigla del idioma del que parte la traducción
     * @param idiomaDestino -Sigla del idioma al que se desea la traducción
     * @return Lista de traducciones exactas. Soporta sinónimos.
     * @throws ExcepcionConsultaErrorSys
     * @throws ExcepcionConexionErrorSys
     */
    public ArrayList<RegistroTraduccionDTO> buscarTraduccion(String textoOrigen, String idiomaOrigen, String idiomaDestino) throws ExcepcionConsultaErrorSys, ExcepcionConexionErrorSys {
        textoOrigen = textoOrigen.toLowerCase();
        idiomaOrigen = idiomaOrigen.toLowerCase();
        idiomaDestino = idiomaDestino.toLowerCase();

        try {
            conexionDB = daoFactory.getConexion();
            String query1 = this.buscarTraduccionQuery;
            PreparedStatement preStat = conexionDB.prepareStatement(query1);
            preStat.setString(1, textoOrigen);
            preStat.setString(2, idiomaOrigen);
            preStat.setString(3, idiomaDestino);
            preStat.setString(4, idiomaDestino);
            preStat.setString(5, textoOrigen);
            preStat.setString(6, idiomaOrigen);
            ResultSet rSet = preStat.executeQuery();

            ArrayList<RegistroTraduccionDTO> traducciones = this.registrosDTOs(rSet);
            ArrayList<RegistroTraduccionDTO> nuevasTraducciones = new ArrayList<RegistroTraduccionDTO>();
            String textoOrigResult;
            String idiomaOrigResult;
            for (RegistroTraduccionDTO unRegistroTraduccionDTO : traducciones) {
                textoOrigResult = unRegistroTraduccionDTO.getTextoOrigen();
                idiomaOrigResult = unRegistroTraduccionDTO.getIdiomaOrigen();

                if (!textoOrigResult.equalsIgnoreCase(textoOrigen)
                        && !idiomaOrigResult.equalsIgnoreCase(idiomaOrigen)) {
                    nuevasTraducciones.add(this.reformatTraduccion(unRegistroTraduccionDTO));
                } else {
                    nuevasTraducciones.add(unRegistroTraduccionDTO);
                }
            }
            return nuevasTraducciones;
        } catch (SQLException ex) {
            throw new ExcepcionConsultaErrorSys();
        } finally {
            daoFactory.cerrarConexion(conexionDB);
        }
    }

    /**
     * Obtiene de la Factory actual la traducción apróximada para el texto de entrada.
     * Retorna aquellas traducciones en la que en el texto de entrada está incluido
     * el texto pasado por parámetro.
     * @param textoOrigen -Texto a traducir
     * @param idiomaOrigen -Sigla del idioma del que parte la traducción
     * @param idiomaDestino -Sigla del idioma al que se desea la traducción
     * @return Lista de traducciones apróximadas.
     * @throws ExcepcionConsultaErrorSys
     * @throws ExcepcionConexionErrorSys
     */
    public ArrayList<RegistroTraduccionDTO> buscarAproximacion(String textoOrigen, String idiomaOrigen, String idiomaDestino) throws ExcepcionConsultaErrorSys, ExcepcionConexionErrorSys {

        textoOrigen = textoOrigen.toLowerCase();
        idiomaOrigen = idiomaOrigen.toLowerCase();
        idiomaDestino = idiomaDestino.toLowerCase();
        try {
            conexionDB = daoFactory.getConexion();
            String query = this.buscarAproximacionQuery;
            PreparedStatement preStat = conexionDB.prepareStatement(query);
            preStat.setString(1, idiomaOrigen);
            preStat.setString(2, textoOrigen + " %");
            preStat.setString(3, "% " + textoOrigen + " %");
            preStat.setString(4, "% " + textoOrigen);
            preStat.setString(5, idiomaOrigen);
            preStat.setString(6, textoOrigen + " %");
            preStat.setString(7, "% " + textoOrigen + " %");
            preStat.setString(8, "% " + textoOrigen);
            preStat.setString(9, idiomaDestino);
            preStat.setString(10, idiomaDestino);

            ResultSet rSet = preStat.executeQuery();

            ArrayList<RegistroTraduccionDTO> traducciones = this.registrosDTOs(rSet);
            ArrayList<RegistroTraduccionDTO> nuevasTraducciones = new ArrayList<RegistroTraduccionDTO>();
            String textoOrigResult;
            String idiomaOrigResult;
            for (RegistroTraduccionDTO unRegistroTraduccionDTO : traducciones) {
                textoOrigResult = unRegistroTraduccionDTO.getTextoOrigen();
                idiomaOrigResult = unRegistroTraduccionDTO.getIdiomaOrigen();

                if (!textoOrigResult.equalsIgnoreCase(textoOrigen)
                        && !idiomaOrigResult.equalsIgnoreCase(idiomaOrigen)) {
                    nuevasTraducciones.add(this.reformatTraduccion(unRegistroTraduccionDTO));
                } else {
                    nuevasTraducciones.add(unRegistroTraduccionDTO);
                }
            }
            return nuevasTraducciones;
        } catch (SQLException ex) {
            throw new ExcepcionConsultaErrorSys();
        } finally {
            daoFactory.cerrarConexion(conexionDB);
        }
    }

    /**
     * Genera una lista de traduccónes que fueron recuperadas de el
     * resultado de una consulta sobre una base de datos
     * @param rSet -ResultSet obtenido de consultar en la base de datos
     * @return Array de traducciones
     * @throws SQLException
     */
    protected ArrayList<RegistroTraduccionDTO> registrosDTOs(ResultSet rSet) throws SQLException {
        ArrayList<RegistroTraduccionDTO> registros = new ArrayList();
        while (rSet.next()) {
            RegistroTraduccionDTO registroDTO = this.mapeoRegistroDTO(rSet);
            registros.add(registroDTO);
        }
        return registros;
    }

    /**
     * Retorna una traducción recuperada del resultado de una
     * consulta sobre una base de datos
     * @param rSet -ResultSet obtenido de consultar en la base de datos
     * @return RegistroTraduccionDTO 
     * @throws SQLException
     */
    protected RegistroTraduccionDTO mapeoRegistroDTO(ResultSet rSet) throws SQLException {
        return new RegistroTraduccionDTO(
                rSet.getString("textoorigen"),
                rSet.getString("idiomaorigen"),
                rSet.getString("textodestino"),
                rSet.getString("idiomadestino"));
    }

    /**
     * Retorna una traducción en la que el texto de origen y el idioma origen deben
     * ser el texto destino y el idoma destino respectivamente.
     * Este método es usado cuando una traducción esta guardada en la base de datos
     * en una disposición diferente.
     * @param unRegistroTraduccionDTO
     * @return una traducción formateada
     */
    private RegistroTraduccionDTO reformatTraduccion(RegistroTraduccionDTO unRegistroTraduccionDTO) {
        String txtOrig = unRegistroTraduccionDTO.getTextoOrigen();
        String idiomaOrig = unRegistroTraduccionDTO.getIdiomaOrigen();
        String txtDest = unRegistroTraduccionDTO.getTextoDestino();
        String idiomaDest = unRegistroTraduccionDTO.getIdiomaDestino();

        RegistroTraduccionDTO nuevoDTO = new RegistroTraduccionDTO(txtDest, idiomaDest, txtOrig, idiomaOrig);
        return nuevoDTO;
    }
}
